//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

namespace Microsoft.Research.ScientificWorkflow.UIDesigner
{
    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Drawing.Imaging;
    using System.Globalization;
    using System.IO;
    using System.Runtime.InteropServices;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using Microsoft.Research.ScientificWorkflow.TridentUtilities;
    using Microsoft.Research.DataLayer;
    using Microsoft.Win32;

    /// <summary>
    /// This file contains the logic to retrieve the Icons from system for 
    /// the selected extensions and stored in static object
    /// </summary>
    public static class DataProductIconBuilder
    {
        /// <summary>
        /// Associates file extensions and BitmapImage objects
        /// </summary>
        private static Dictionary<string, BitmapImage> fileTypeIcons = new Dictionary<string, BitmapImage>();

        /// <summary>
        /// Expected file types
        /// Note: if new file type extension to be supported then it needs to be added here
        /// </summary>
        private static string[] fileTypeExtensions = new string[] { ".txt", ".csv", ".xls", ".html", ".bmp", ".doc", ".xml" };

        /// <summary>
        /// Assoicates MIME types and file extensions
        /// </summary>
        private static Dictionary<string, string> productTypeExtensions = new Dictionary<string, string>();

        /// <summary>
        /// Gets the dictionary containing icons and file extensions
        /// </summary>
        public static Dictionary<string, BitmapImage> FileTypeIcons
        {
            get { return fileTypeIcons; }
        }

        /// <summary>
        /// Gets the dictionary containing file extensions and MIME type
        /// </summary>
        public static Dictionary<string, string> ProductTypeExtensions
        {
            get { return productTypeExtensions; }
        }

        /// <summary>
        /// Initialises the MIME type and file extensions
        /// </summary>
        public static void Initialize()
        {
            productTypeExtensions.Add("application/csv", ".csv");
            productTypeExtensions.Add("application/ms-excel", ".xls");
            productTypeExtensions.Add("text/html", ".html");
            productTypeExtensions.Add("image/jpg", ".bmp");
            productTypeExtensions.Add("image/png", ".bmp");
            productTypeExtensions.Add("text/plain", ".txt");
            productTypeExtensions.Add("application/ms-word", ".doc");
            productTypeExtensions.Add("application/xml", ".xml");
        }

        /// <summary>
        /// Extracts the Icons from system registry for the specified file extensions
        /// </summary>
        public static void ExtractIcons()
        {
            try
            {
                //// clear the collection first
                fileTypeIcons.Clear();

                foreach (string extension in fileTypeExtensions)
                {
                    //// Sample: C:\Windows\Installer\{90120000-0012-0000-0000-0000000FF1CE}\wordicon.exe,13
                    string resourceFile = FindResourceFile(extension);
                    string resourceFilePath = string.Empty;
                    int iconIndex = 0;

                    ParseResourceFile(resourceFile, out resourceFilePath, out iconIndex);

                    //// if file not exists continue for next item
                    if (!File.Exists(resourceFilePath))
                    {
                        continue;
                    }

                    BitmapImage iconImage = ExtractIconBitmap(resourceFilePath, iconIndex);
                    if (iconImage != null)
                    {
                        //// add extension and images
                        fileTypeIcons.Add(extension, iconImage);
                    }
                }
            }
            catch (Exception ex)
            {
                TridentErrorHandler.HandleAndThrowBusinessLogicException(ex, TridentErrorConstants.ErrorNumber90000013, TridentResourceManager.GetString("ExtractIconsException"));
            }
        }

        /// <summary>
        /// parses the resource file string
        /// </summary>
        /// <param name="resourceFile">resource file string</param>
        /// <param name="resourceFilePath">resource file name</param>
        /// <param name="iconIndex">Icon index in the file</param>
        private static void ParseResourceFile(string resourceFile, out string resourceFilePath, out int iconIndex)
        {
            int commaIndex = 0;
            resourceFilePath = resourceFile;
            iconIndex = 0;

            if ((commaIndex = resourceFile.IndexOf(",", StringComparison.OrdinalIgnoreCase)) != -1)
            {
                //// parse file path
                resourceFilePath = resourceFile.Substring(0, commaIndex);

                //// parse icon index
                if (resourceFile.Length > commaIndex)
                {
                    iconIndex = int.Parse(resourceFile.Substring(commaIndex + 1), NumberStyles.Integer, NumberFormatInfo.InvariantInfo);
                }
            }
        }

        /// <summary>
        /// Extracts the icon from the resource file and converts it to WPF BitmapImage object
        /// </summary>
        /// <param name="resourceFile">resource file path</param>
        /// <param name="iconIndex">index of the icon</param>
        /// <returns>BitmapImage object</returns>
        private static BitmapImage ExtractIconBitmap(string resourceFile, int iconIndex)
        {
            BitmapImage iconImage = null;

            if (resourceFile.EndsWith(".ico", StringComparison.OrdinalIgnoreCase))
            {
                iconImage = new BitmapImage(new Uri("file:///" + resourceFile.Replace("\\", "/")));
                return iconImage;
            }

            IntPtr iconHandle = NativeMethods.ExtractIcon(0, resourceFile, iconIndex);

            if (iconHandle != IntPtr.Zero)
            {
                Icon icon = Icon.FromHandle(iconHandle);
                iconImage = new BitmapImage();

                using (MemoryStream stream = new MemoryStream())
                {
                    icon.Save(stream);

                    iconImage.BeginInit();
                    stream.Seek(0, SeekOrigin.Begin);
                    iconImage.StreamSource = stream;
                    iconImage.EndInit();
                }
            }

            return iconImage;
        }

        /// <summary>
        /// finds the resource file string from the registry for the given file extension
        /// </summary>
        /// <param name="extension">file extension</param>
        /// <returns>resource file string</returns>
        private static string FindResourceFile(string extension)
        {
            //// Registry structure for file extension and default icons
            //// Key: Computer\HKEY_CLASSES_ROOT\.docx (Default) - Word.Document.12
            //// Key: Computer\HKEY_CLASSES_ROOT\Word.Document.12\DefaultIcon (Default) - C:\Windows\Installer\{90120000-0012-0000-0000-0000000FF1CE}\wordicon.exe,13
            using (RegistryKey extensionKey = Registry.ClassesRoot.OpenSubKey(extension))
            {
                if (extensionKey == null)
                {
                    return string.Empty;
                }

                object extensionKeyDefaultValue = extensionKey.GetValue(string.Empty);
                if (extensionKeyDefaultValue == null)
                {
                    return string.Empty;
                }

                using (RegistryKey defaultValueKey = Registry.ClassesRoot.OpenSubKey(extensionKeyDefaultValue.ToString()))
                {
                    RegistryKey defaultIconKey;
                    using (defaultIconKey = defaultValueKey.OpenSubKey("DefaultIcon"))
                    {
                        if (defaultIconKey == null)
                        {
                            //// Special case for .pdf extension
                            //// Key: Computer\HKEY_CLASSES_ROOT\.pdf (Default) - AcroExch.Document
                            //// Key: Computer\HKEY_CLASSES_ROOT\AcroExch.Document\CurVer (Default) - AcroExch.Document.7
                            //// Key: Computer\HKEY_CLASSES_ROOT\AcroExch.Document.7\DefaultIcon (Default) - C:\Windows\Installer\{AC76BA86-7AD7-1033-7B44-A81200000003}\PDFFile_8.ico,0
                            using (RegistryKey curVerKey = Registry.ClassesRoot.OpenSubKey(extensionKeyDefaultValue.ToString() + "\\CurVer"))
                            {
                                object curVerValue = curVerKey.GetValue(string.Empty);
                                defaultIconKey = Registry.ClassesRoot.OpenSubKey(curVerValue.ToString() + "\\DefaultIcon");
                            }
                        }

                        object defaultIconValue = defaultIconKey.GetValue(string.Empty);
                        if (defaultIconValue.ToString().Contains("%1"))
                        {
                            //// Special case when DefaultIcon key does not available in the registry 
                            //// Key: Computer\HKEY_CLASSES_ROOT\.txt (Default) - txtfile
                            //// Key: Computer\HKEY_CLASSES_ROOT\txtfile\FriendlyTypeName (Default) - @%SystemRoot%\system32\notepad.exe,-469
                            object friendlyTypeNameValue = defaultValueKey.GetValue("FriendlyTypeName");
                            if (friendlyTypeNameValue != null)
                            {
                                return friendlyTypeNameValue.ToString().Replace("@", string.Empty);
                            }
                        }

                        return defaultIconValue.ToString();
                    }
                }
            }
        }        
    }
}
